package cn.com.jcoo.security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;

/**
 * SecurityUtils：基于BouncyCastle库（BC） ------------------ JCA Java密码架构 提供者 -> 引擎 -> 算法
 * Provider：Security提供者 Engine类: 引擎类为特定类型的密码服务提供接口，而不依赖于特定的密码算法或提供者。 引擎需要提供如下功能： -
 * 密码操作（加密，数字签名，消息摘要等）， - 发生器或密码材料的转换器（密钥和算法参数） - 对象（密钥库或证书）封装了密码数据，可以在更高的抽象层使用。
 * <p>
 * SecureRandom 用于生成随机或伪随机数字。 MessageDigest 用于计算指定数据的消息摘要（散列）。 Signature
 * 使用密钥初始化，这些签名用于签署数据并验证数字签 Cipher 用密钥初始化，用于加密/解密数据。存在各种类型的算法 Message Authentication
 * Codes（MAC） 与MessageDigests一样，它们也会生成散列值，但是首先使用密钥初始化以保护消息的完整性。 KeyFactory
 * 用于将Key类型的现有不透明密钥转换为密钥规范（底层密钥材料的透明表示），反之亦然。 SecretKeyFactory
 * 用于将SecretKey类型的现有不透明加密密钥转换为密钥规范（底层密钥材料的透明表示），反之亦然。
 * SecretKeyFactorys是专门的KeyFactorys，只能创建密钥（对称）。 KeyPairGenerator 用于生成一对适用于指定算法的公钥和私钥。
 * KeyGenerator 用于生成与指定算法一起使用的新密钥。 KeyAgreement 由两方或多方使用，商定和建立一个特定的密钥，用于特定的密码操作。
 * AlgorithmParameters 用于存储特定算法的参数，包括参数编码和解码。 AlgorithmParameterGenerator
 * 用于生成适合于指定算法的一组AlgorithmParameters。 KeyStore
 * 用于创建和管理密钥库。密钥库是密钥的数据库。密钥库中的私钥具有与其关联的证书链，用于验证相应的公钥。密钥库还包含来自可信实体的证书。 CertificateFactory
 * 用于创建公钥证书和证书吊销列表（CRL）。 CertPathBuilder 用于构建证书链（也称为证书路径）。 CertPathValidator 用于验证证书链。
 * CertStore 用于从存储库中检索证书和CRL。 注意 生成器可以创建具有全新内容的对象，而工厂只能从现有材料（例如编码）中创建对象。 AES/CBC/NoPadding
 * (128) AES/CBC/PKCS5Padding (128) AES/ECB/NoPadding (128) AES/ECB/PKCS5Padding (128)
 * DES/CBC/NoPadding (56) DES/CBC/PKCS5Padding (56) DES/ECB/NoPadding (56)
 * DES/ECB/PKCS5Padding (56) DESede/CBC/NoPadding (168) DESede/CBC/PKCS5Padding (168)
 * DESede/ECB/NoPadding (168) DESede/ECB/PKCS5Padding (168) RSA/ECB/PKCS1Padding (1024,
 * 2048) RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
 * RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048) (1)加密算法有：AES，DES，DESede(DES3)和RSA 四种
 * (2) 模式有CBC(有向量模式)和ECB(无向量模式)，向量模式可以简单理解为偏移量，使用CBC模式需要定义一个IvParameterSpec对象 (3) 填充模式:
 * NoPadding: 加密内容不足8位用0补足8位, Cipher类不提供补位功能，需自己实现代码给加密内容添加0, 如{65,65,65,0,0,0,0,0}
 * PKCS5Padding: 加密内容不足8位用余位数补足8位, 如{65,65,65,5,5,5,5,5}或{97,97,97,97,97,97,2,2}; 刚好8位补8位8
 * @author Jinty
 * @since 1.0
 */
public class SecurityUtils {
	private static Provider provider;

	static {
		// 使用BouncyCastle库提供的加解密服务
		provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
		if (provider == null) {
			provider = new BouncyCastleProvider();
			Security.addProvider(provider);
		}
	}

	/**
	 * 生成指定长度的随机向量
	 */
	public static byte[] generateIV(String seed, int ivSize)
			throws NoSuchAlgorithmException {
		byte[] bytes = new byte[ivSize];
		SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
		if (null != seed && !"".equals(seed)) {
			// 设置随机数种子
			random.setSeed(seed.getBytes());
		}
		random.nextBytes(bytes);
		return bytes;
	}

	/**
	 * 生成指定长度的随机密钥
	 *
	 * @param algorithmName 算法
	 * @param seed 随机数种子
	 * @param keySize 密钥大小
	 * @return 密钥
	 */
	public static SecretKey generateKey(String algorithmName, String seed, int keySize)
			throws NoSuchAlgorithmException {
		// 密钥生成器
		KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithmName, provider);
		// 随机数
		SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
		if (null != seed && !"".equals(seed)) {
			// 设置随机数种子
			random.setSeed(seed.getBytes());
		}
		// 密钥生成引擎初始化
		keyGenerator.init(keySize, random);
		// 生成密钥
		return keyGenerator.generateKey();
	}

	/**
	 * 生成密钥对
	 */
	public static KeyPair generatorKeyPair(String algorithmName,
			AlgorithmParameterSpec algorithmParameterSpec)
			throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithmName,
				provider);
		keyPairGenerator.initialize(algorithmParameterSpec);
		return keyPairGenerator.generateKeyPair();
	}

	/**
	 * 生成指定算法的加解密处理器
	 *
	 * @param algorithm 完整算法名
	 * @param cipherMode 处理器模式
	 * @param key 秘钥
	 */
	public static Cipher generateCipher(String algorithm, int cipherMode, Key key)
			throws Exception {
		// 加解密执行器
		Cipher cipher = Cipher.getInstance(algorithm, provider);
		// 加解密引擎初始化
		cipher.init(cipherMode, key);
		return cipher;
	}

	/**
	 * 生成指定算法的加解密处理器
	 *
	 * @param algorithm 完整算法名
	 * @param cipherMode 处理器模式
	 * @param key 秘钥
	 * @param parameterSpec 参数
	 */
	public static Cipher generateCipher(String algorithm, int cipherMode, Key key,
			AlgorithmParameterSpec parameterSpec) throws Exception {
		// 加解密执行器
		Cipher cipher = Cipher.getInstance(algorithm, provider);
		cipher.init(cipherMode, key, parameterSpec);
		return cipher;
	}
}
